using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Windows.Forms;
using ArlesInterfaces.Render;
using ArlesInterfaces.Editor;

namespace Render
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game : Microsoft.Xna.Framework.Game, IRender
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        IEditor editor;
        //Model terrain;
        FillMode fillMode;
        ICamera camera;
        KeyboardState previousKeyState;
        PerfCounter fpsCounter;
        bool fpsLimit;

        #region Properties

        public FillMode RenderFillMode
        {
            set { fillMode = value; }
            get { return fillMode; }
        }

        public MultiSampleType RenderMultiSampleType
        {
            set 
            {
                PresentationParameters pp = GraphicsDevice.PresentationParameters.Clone();
                pp.BackBufferHeight = editor.MainForm.Canvas.Height;
                pp.BackBufferWidth = editor.MainForm.Canvas.Width;
                pp.MultiSampleType = value;
                GraphicsDevice.Reset(pp);
            }
            get { return GraphicsDevice.PresentationParameters.MultiSampleType; }
        }

        public GraphicsDevice Device
        {
            get { return GraphicsDevice; }
        }

        public ContentManager ContentManager
        {
            get { return this.Content; }
        }

        public ICamera Camera
        {
            get { return camera; }
        }

        public bool ShowStats
        {
            get { return this.fpsCounter.Active; }
            set { this.fpsCounter.Active = value; }
        }

        public void ResetStats() 
        {
            fpsCounter.ResetStats();
        }

        public bool FPSLimit
        {
            get { return fpsLimit; }
            set 
            {
                if (value)
                {
                    this.IsFixedTimeStep = true;
                    graphics.SynchronizeWithVerticalRetrace = true;
                    fpsLimit = true;
                }
                else
                {
                    this.IsFixedTimeStep = false;
                    graphics.SynchronizeWithVerticalRetrace = false;
                    fpsLimit = false;
                }
            }
        }

        #endregion

        #region Initialize()
        public Game(IEditor editor)
        {
            camera = new Camera(new Vector3(0, 50, 100));

            this.editor = editor;
            Form xnaWindow = (Form)Control.FromHandle((this.Window.Handle));
            xnaWindow.GotFocus += new EventHandler(xnaWindow_GotFocus);
            editor.MainForm.Canvas.Resize += new EventHandler(Canvas_Resize);
            editor.MainForm.Canvas.GotFocus += new EventHandler(Canvas_GotFocus);
            editor.MainForm.Canvas.LostFocus += new EventHandler(Canvas_LostFocus);
            
            graphics = new GraphicsDeviceManager(this);
            graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
            Content.RootDirectory = "Content";
            fillMode = FillMode.Solid;

            fpsCounter = new PerfCounter(this);
            Components.Add(fpsCounter);

            this.IsFixedTimeStep = false;
            graphics.SynchronizeWithVerticalRetrace = false;

            fpsLimit = true;

        }


        void Canvas_LostFocus(object sender, EventArgs e)
        {
            camera.CameraActive = false;
        }

        void Canvas_GotFocus(object sender, EventArgs e)
        {
            camera.CameraActive = true;
        }

        void Canvas_Resize(object sender, EventArgs e)
        {
            if (GraphicsDevice != null && editor.MainForm.Canvas.Width != 0)
            {
                int width = editor.MainForm.Canvas.Width;
                int height = editor.MainForm.Canvas.Height;
                graphics.PreferredBackBufferWidth = width;
                graphics.PreferredBackBufferHeight = height;
                camera.AspectRatio = (float)(width) / (float)(height);
                graphics.ApplyChanges();
            }
        }

        void xnaWindow_GotFocus(object sender, EventArgs e)
        {
            ((Form)sender).Visible = false;
            editor.MainForm.TopMost = false;
        }

        void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
        {
            e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = editor.MainForm.Canvas.Handle;
            Mouse.WindowHandle = editor.MainForm.Canvas.Handle; 
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            Canvas_Resize(null, null);

            base.Initialize();
        } 

        #endregion

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //terrain = Content.Load<Model>("Models/Terrain");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            #region InputKeyboard
            KeyboardState keyState = Keyboard.GetState();

            if ((keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl) &&
                keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Z)) &&
                !(previousKeyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl) &&
                previousKeyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Z)))
                editor.Invoker.Undo();
            if ((keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl) &&
                keyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Y)) &&
                !(previousKeyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl) &&
                previousKeyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Y)))
                editor.Invoker.Redo();

            previousKeyState = keyState;
            #endregion

            camera.Update(gameTime);

            //Update Scene
            foreach (ISceneObject SceneObject in editor.Scene.Objects)
                SceneObject.Update(gameTime);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

            GraphicsDevice.RenderState.FillMode = fillMode;

            //SortScene
            editor.Scene.Objects.Sort(delegate(ISceneObject so1, ISceneObject so2) { return so1.Tag.CompareTo(so2.Tag); });

            //Draw Scene
            foreach (ISceneObject SceneObject in editor.Scene.Objects)
                SceneObject.Draw(gameTime);

            #region Draw Terrain
            /*
            foreach (ModelMesh mesh in terrain.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;
                    effect.World = camera.World;
                    effect.View = camera.View;
                    effect.Projection = camera.Projection;
                }
                mesh.Draw();
            } 
             * */
            #endregion

            base.Draw(gameTime);
        }
    }
}
